const { exec } = require('child_process')

const _execa = require('execa')
const minimist = require('minimist')

function getDbgWithNamespace(namespace) {
  // 放在函数内，为了晚点执行，让 setEnv 优先执行，以便 debug 读取 process.env.DEBUG
  return require('debug')(`@slardar/script:${namespace}`)
}

/**
 * set process.env
 * @param {*} key
 * @param {*} value
 * @param {*} force
 */
function setEnv(key, value, force) {
  const env = process.env
  ;(!env[key] || force) && (env[key] = value)
}

setEnv('DEBUG', '*')
const execaDebug = getDbgWithNamespace('execa')
const errorConsole = getDbgWithNamespace('error')

/**
 * execa based on child_process.spawn. There is a post show what different between spawn and exec. https://www.hacksparrow.com/nodejs/difference-between-spawn-and-exec-of-node-js-child-rocess.html
 * @param  {...any} args
 * @returns
 */
function execa(...args) {
  const [cmd, params, options] = args
  execaDebug(`${cmd} ${params.join(' ')} ${options ? JSON.stringify(options) : ''}`)
  return _execa.apply(null, args)
}

async function executeCommand(command, options = {}) {
  return new Promise((resolve, reject) => {
    execaDebug(`${command} ${options ? JSON.stringify(options) : ''}`)

    const child = exec(
      command,
      {
        maxBuffer: Infinity,
        ...options,
      },
      (error, stdout) => {
        if (error) {
          // 如果命令执行失败，则将错误信息作为 reject 的结果返回，stdout 也要抛出
          errorConsole(error)
          reject({ stdout: stdout.trim(), error })
        } else {
          // 否则将标准输出的结果作为 resolve 的结果返回
          resolve(stdout.trim()) // trim() 可以去掉末尾的空行
        }
      },
    )

    // 监听错误信息（比如命令没有找到），并将错误信息作为 reject 的结果返回
    child.on('error', (error) => {
      reject({ error })
    })

    // 将标准错误信息打印到控制台（可选）
    if (options.printStderr) {
      child.stderr.pipe(process.stderr)
    }
  })
}

function getArgv() {
  return minimist(process.argv.slice(2))
}

module.exports = {
  execa,
  setEnv,
  getDbgWithNamespace,
  executeCommand,
  getArgv,
}
